///////////////////////////
//
// File: bcf_menu_filter.cpp
//
// Author: Brian Fehrman
//
// Purpose: Contains the class implementation for the Filter Menu
//
///////////////////////////

#include "bcf_img_proc.h"

///////////////////////////
// Function: Apply_Correlation_Filter
//
// Author: Brian Fehrman
//
// Purpose: This function takes a correlation filter and applies it to the given image
///////////////////////////
void BCF_Img_Proc::Apply_Correlation_Filter( Image &image, float **filter, const int NSIZE )
{
    int nrows = image.Height();
    int ncols = image.Width();
    int nsize_div_2 = NSIZE / 2;
    Image temp_image = image;

    //Traverse each pixel in matrix, being careful not to overstep bounds
    for( int img_row= 0; img_row < ( nrows - NSIZE ); img_row++ )
    {
        for( int img_col = 0; img_col < ( ncols - NSIZE ); img_col++ )
        {
            float sum = 0.0;

            //Apply the correlation filter to the current neighborhood
            for( int filt_row = 0; filt_row < NSIZE; filt_row++ )
            {
                for( int filt_col = 0; filt_col < NSIZE; filt_col++ )
                {

                   float pix_val = image[ img_row + filt_row ][ img_col + filt_col ];

                   sum += pix_val * filter[ filt_row ][ filt_col ];

                }//end filt_col loop

            }//end filt_row loop

            temp_image[ img_row + nsize_div_2 ][ img_col + nsize_div_2 ] = sum + 0.5;

        }//end img_col loop

    }//end img_row loop

    image = temp_image;
}

///////////////////////////
// Function: my_sort
//
// Author: Brian Fehrman
//
// Purpose: Integer comparator function for the STL sort function
///////////////////////////
bool my_sort (int i,int j) { return (i<j); }


///////////////////////////
// Function: Filter_Emboss
//
// Author: Brian Fehrman
//
// Purpose: embosss the image by using a 3x3 embossing filter (pre-defined) and
//          then adjusts the image contrast by calling Dr. Weiss's histogram
//          equalization function.
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Emboss( Image &image )
{
    if ( image.IsNull() ) return false;

    const int NSIZE = 3;

    //Create emboss filter and pass it to the Auto_Correlation_Filter function
    //This is could be made more efficient since it really just needs to perform
    //a simple subtraction instead of 9 multiplies and 9 additions
    float **emboss_filter;

    emboss_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        emboss_filter[ row ] = new float[ NSIZE ];
    }

    emboss_filter[0][0] =  0;
    emboss_filter[0][1] =  0;
    emboss_filter[0][2] =  0;

    emboss_filter[1][0] =  0;
    emboss_filter[1][1] =  1;
    emboss_filter[1][2] =  0;

    emboss_filter[2][0] =  0;
    emboss_filter[2][1] =  0;
    emboss_filter[2][2] = -1;

    Apply_Correlation_Filter( image, emboss_filter, NSIZE );

    histogramEqualize( image );

    for( int row = 0; row < NSIZE; row++ )
    {
       delete emboss_filter[ row ];
    }

    delete [] emboss_filter;

    return true;
}

///////////////////////////
// Function: Filter_Maximum
//
// Author: Brian Fehrman
//
// Purpose: Filters by choosing the maximum from a neighborhood whose size is user specified.
//          The pixel on which the neighborhood is centered is set to the max value found
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Maximum( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image temp_image = image;
    int NSIZE = 3;
    int nsize_div_2 = NSIZE / 2;

    Dialog dlg( "Choose N size");
    dlg.Add(NSIZE, "N Size");

    if( dlg.Show() )
    {
        nsize_div_2 = NSIZE / 2;

        //Traverse each pixel in matrix, being careful not to overstep bounds
        for( int img_row= 0; img_row < ( nrows - NSIZE ); img_row++ )
        {
            for( int img_col = 0; img_col < ( ncols - NSIZE ); img_col++ )
            {
                float max = -1000;

                //Get the max of the current neighborhood
                for( int filt_row = 0; filt_row < NSIZE; filt_row++ )
                {
                    for( int filt_col = 0; filt_col < NSIZE; filt_col++ )
                    {
                        if( image[ img_row + filt_row ][ img_col + filt_col ] > max )
                        {
                            max = image[ img_row + filt_row ][ img_col + filt_col ];
                        }

                    }//end filt_col loop

                }//end filt_row loop

                temp_image[ img_row + nsize_div_2 ][ img_col + nsize_div_2 ] = max;

            }//end img_col loop

        }//end img_row loop

        image = temp_image;
    }

    return true;
}

///////////////////////////
// Function: Filter_Mean
//
// Author: Brian Fehrman
//
// Purpose: Filters by finding the mean in a neighborhood whose size is user specified.
//          The pixel on which the neighborhood is centered is set to the mean value
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Mean( Image &image )
{
    if ( image.IsNull() ) return false;

    int NSIZE = 3;
    float **mean_filter;

    Dialog dlg( "Choose N size");
    dlg.Add(NSIZE, "N Size");

    if( dlg.Show() )
    {
        //Create the mean filter, which is passed to the Apply_Correlation_Filter function
        mean_filter = new float*[ NSIZE ];

        for( int row = 0; row < NSIZE; row++ )
        {
            mean_filter[ row ] = new float[ NSIZE ];
        }

        for( int row = 0; row < NSIZE; row++ )
        {
            for( int col = 0; col < NSIZE; col++ )
            {
                mean_filter[ row ][ col ] = 1.0 / ( NSIZE * NSIZE );
            }
        }

        Apply_Correlation_Filter( image, mean_filter, NSIZE );

        for( int row = 0; row < NSIZE; row++ )
        {
           delete mean_filter[ row ];
        }

        delete [] mean_filter;
    }

    return true;
}

///////////////////////////
// Function: Filter_Median
//
// Author: Brian Fehrman
//
// Purpose: Filters by choosing the median from a neighborhood whose size is user specified.
//          The pixel on which the neighborhood is centered is set to the median value found
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Median( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image temp_image = image;
    int NSIZE = 3;
    int nsize_div_2 = NSIZE / 2;

    Dialog dlg( "Choose N size");
    dlg.Add(NSIZE, "N Size");

    if( dlg.Show() )
    {
        nsize_div_2 = NSIZE / 2;

        std::vector<int> values;
        values.resize( NSIZE );

        //Traverse each pixel in matrix, being careful not to overstep bounds
        for( int img_row= 0; img_row < ( nrows - NSIZE ); img_row++ )
        {
            for( int img_col = 0; img_col < ( ncols - NSIZE ); img_col++ )
            {
                //Flatten the neighborhood to a 1D array so that we can use
                //the STL sort function
                for( int filt_row = 0; filt_row < NSIZE; filt_row++ )
                {
                    for( int filt_col = 0; filt_col < NSIZE; filt_col++ )
                    {
                       values[ filt_row + filt_col ]= image[ img_row + filt_row ][ img_col + filt_col ];

                    }//end filt_col loop

                }//end filt_row loop

                std::sort( values.begin(), values.end(), my_sort );

                temp_image[ img_row + nsize_div_2 ][ img_col + nsize_div_2 ] = values[ nsize_div_2 ];

            }//end img_col loop

        }//end img_row loop

        image = temp_image;
    }

    return true;
}

///////////////////////////
// Function: Filter_Minimum
//
// Author: Brian Fehrman
//
// Purpose: Filters by choosing the min from a neighborhood whose size is user specified.
//          The pixel on which the neighborhood is centered is set to the min value found
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Minimum( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image temp_image = image;
    int NSIZE = 3;
    int nsize_div_2 = NSIZE / 2;

    Dialog dlg( "Choose N size");
    dlg.Add(NSIZE, "N Size");

    if( dlg.Show() )
    {
        nsize_div_2 = NSIZE / 2;

        //Traverse each pixel in matrix, being careful not to overstep bounds
        for( int img_row= 0; img_row < ( nrows - NSIZE ); img_row++ )
        {
            for( int img_col = 0; img_col < ( ncols - NSIZE ); img_col++ )
            {
                float min = 1000;

                //Find the min of the neighborhood
                for( int filt_row = 0; filt_row < NSIZE; filt_row++ )
                {
                    for( int filt_col = 0; filt_col < NSIZE; filt_col++ )
                    {
                        if( image[ img_row + filt_row ][ img_col + filt_col ] < min )
                        {
                            min = image[ img_row + filt_row ][ img_col + filt_col ];
                        }

                    }//end filt_col loop

                }//end filt_row loop

                temp_image[ img_row + nsize_div_2 ][ img_col + nsize_div_2 ] = min;

            }//end img_col loop

        }//end img_row loop

        image = temp_image;
    }

    return true;
}

///////////////////////////
// Function: Filter_Plus_Mean
//
// Author: Brian Fehrman
//
// Purpose: Filters by finding the mean of a neighborhood using a "plus" shaped mask. The
//          value of the pixel on which the neighborhood is centered will be replaced with
//          the mean
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Plus_Mean( Image &image )
{
    if ( image.IsNull() ) return false;

    const int NSIZE = 3;

    //Create plus shaped mean filter and pass it to the Auto_Correlation_Filter function
    float **plus_mean_filter;

    plus_mean_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        plus_mean_filter[ row ] = new float[ NSIZE ];
    }

    plus_mean_filter[0][0] =  0;
    plus_mean_filter[0][1] =  1;
    plus_mean_filter[0][2] =  0;

    plus_mean_filter[1][0] =  1;
    plus_mean_filter[1][1] =  1;
    plus_mean_filter[1][2] =  1;

    plus_mean_filter[2][0] =  0;
    plus_mean_filter[2][1] =  1;
    plus_mean_filter[2][2] =  0;

    Apply_Correlation_Filter( image, plus_mean_filter, NSIZE );

    for( int row = 0; row < NSIZE; row++ )
    {
       delete plus_mean_filter[ row ];
    }

    delete [] plus_mean_filter;

    return true;
}

///////////////////////////
// Function: Filter_Range
//
// Author: Brian Fehrman
//
// Purpose: Filters by finding the (max - min) value from a neighborhood whose size is user specified.
//          The pixel on which the neighborhood is centered is set to the (max - min) value found
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Range( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    Image temp_image = image;
    int NSIZE = 3;
    int nsize_div_2 = NSIZE / 2;

    Dialog dlg( "Choose N size");
    dlg.Add(NSIZE, "N Size");

    if( dlg.Show() )
    {
        nsize_div_2 = NSIZE / 2;

        //Traverse each pixel in matrix, being careful not to overstep bounds
        for( int img_row= 0; img_row < ( nrows - NSIZE ); img_row++ )
        {
            for( int img_col = 0; img_col < ( ncols - NSIZE ); img_col++ )
            {
                float max = -1000;
                float min = 1000;

                //Find the min and max in the neighborhood
                for( int filt_row = 0; filt_row < NSIZE; filt_row++ )
                {
                    for( int filt_col = 0; filt_col < NSIZE; filt_col++ )
                    {
                        if( image[ img_row + filt_row ][ img_col + filt_col ] > max )
                        {
                            max = image[ img_row + filt_row ][ img_col + filt_col ];
                        }

                        if( image[ img_row + filt_row ][ img_col + filt_col ] < min )
                        {
                            min = image[ img_row + filt_row ][ img_col + filt_col ];
                        }

                    }//end filt_col loop

                }//end filt_row loop

                temp_image[ img_row + nsize_div_2 ][ img_col + nsize_div_2 ] = max - min;

            }//end img_col loop

        }//end img_row loop

        image = temp_image;
    }

    return true;
}

///////////////////////////
// Function: Filter_Sharpen
//
// Author: Brian Fehrman
//
// Purpose: Sharpens the image by using a 3x3 sharpening filter (pre-defined)
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Sharpen( Image &image )
{
    if ( image.IsNull() ) return false;

    const int NSIZE = 3;

    //Create sharpen filter and pass it to the Auto_Correlation_Filter function
    float **sharpen_filter;

    sharpen_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        sharpen_filter[ row ] = new float[ NSIZE ];
    }

    sharpen_filter[0][0] =  0;
    sharpen_filter[0][1] = -1;
    sharpen_filter[0][2] =  0;

    sharpen_filter[1][0] = -1;
    sharpen_filter[1][1] =  5;
    sharpen_filter[1][2] = -1;

    sharpen_filter[2][0] =  0;
    sharpen_filter[2][1] = -1;
    sharpen_filter[2][2] =  0;

    Apply_Correlation_Filter( image, sharpen_filter, NSIZE );

    for( int row = 0; row < NSIZE; row++ )
    {
       delete sharpen_filter[ row ];
    }

    delete [] sharpen_filter;

    return true;
}

///////////////////////////
// Function: Filter_Smooth
//
// Author: Brian Fehrman
//
// Purpose: Sharpens the image by using a 3x3 sharpening filter (pre-defined)
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_Smooth( Image &image )
{
    if ( image.IsNull() ) return false;

    const int NSIZE = 3;
    float scale = 16.0;

    //Create smooth filter and pass it to the Auto_Correlation_Filter function
    float **smooth_filter;

    smooth_filter = new float*[ NSIZE ];

    for( int row = 0; row < NSIZE; row++ )
    {
        smooth_filter[ row ] = new float[ NSIZE ];
    }

    smooth_filter[0][0] = 1 / scale;
    smooth_filter[0][1] = 2 / scale;
    smooth_filter[0][2] = 1 / scale;

    smooth_filter[1][0] = 2 / scale;
    smooth_filter[1][2] = 4 / scale;
    smooth_filter[1][2] = 2 / scale;

    smooth_filter[2][0] = 1 / scale;
    smooth_filter[2][1] = 2 / scale;
    smooth_filter[2][2] = 1 / scale;

    Apply_Correlation_Filter( image, smooth_filter, NSIZE );


    for( int row = 0; row < NSIZE; row++ )
    {
       delete smooth_filter[ row ];
    }

    delete [] smooth_filter;

    return true;
}

///////////////////////////
// Function: Filter_STD
//
// Author: Brian Fehrman
//
// Purpose: Filters by finding the STD in a neighborhood whose size is user specified.
//          The pixel on which the neighborhood is centered is set to the STD value
///////////////////////////
bool BCF_Img_Proc::Menu_Filter_STD( Image &image )
{
    if ( image.IsNull() ) return false;

    int nrows = image.Height();
    int ncols = image.Width();
    int NSIZE = 3;
    int nsize_div_2 = NSIZE / 2;
    float **mean_filter;

    Dialog dlg( "Choose N size");
    dlg.Add(NSIZE, "N Size");

    if( dlg.Show() )
    {
        Image mean_image = image;
        Image temp_image = image;

        //Create the mean filter, which is passed to the Apply_Correlation_Filter function
        mean_filter = new float*[ NSIZE ];

        for( int row = 0; row < NSIZE; row++ )
        {
            mean_filter[ row ] = new float[ NSIZE ];
        }

        for( int row = 0; row < NSIZE; row++ )
        {
            for( int col = 0; col < NSIZE; col++ )
            {
                mean_filter[ row ][ col ] = 1.0 / ( NSIZE * NSIZE );
            }
        }

        Apply_Correlation_Filter( mean_image, mean_filter, NSIZE );

        for( int row = 0; row < NSIZE; row++ )
        {
           delete mean_filter[ row ];
        }

        delete [] mean_filter;

        nsize_div_2 = NSIZE / 2;

        //Traverse each pixel in matrix, being careful not to overstep bounds
        for( int img_row= 0; img_row < ( nrows - NSIZE ); img_row++ )
        {
            for( int img_col = 0; img_col < ( ncols - NSIZE ); img_col++ )
            {
                float standard_deviation = 0.0;

                //Find the standard deviation of the neighborhood
                for( int filt_row = 0; filt_row < NSIZE; filt_row++ )
                {
                    for( int filt_col = 0; filt_col < NSIZE; filt_col++ )
                    {
                        int diff = image[ img_row + filt_row ][ img_col + filt_col ] - mean_image[ img_row + filt_row ][ img_col + filt_col ];

                        standard_deviation += diff * diff;

                    }//end filt_col loop

                }//end filt_row loop

                standard_deviation = sqrt( standard_deviation );

                temp_image[ img_row + nsize_div_2 ][ img_col + nsize_div_2 ] = standard_deviation;

            }//end img_col loop

        }//end img_row loop

        image = temp_image;
    }

    return true;
}
